package com.zj.eswrapper.interfaces.impl;

import com.zj.eswrapper.enums.BoolEnum;
import com.zj.eswrapper.enums.OperatorEnum;
import com.zj.eswrapper.interfaces.IESearchWrapper;
import com.zj.eswrapper.interfaces.IEsQueryWrapper;
import com.zj.eswrapper.model.Bool;

import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

/**
 * es基础查询拼接器抽象类
 *
 * @author arthur_zhou
 * @date 2023/12/28 18:26
 */
public abstract class AbstractEsQueryWrapper<Field,
        QueryWrapper extends AbstractEsQueryWrapper<Field, QueryWrapper, SearchWrapper, AggWrapper, QueryBuilder, SearchBuilder, AggBuilder>,
        SearchWrapper extends AbstractEsSearchWrapper<Field, SearchWrapper, SearchBuilder>,
        AggWrapper extends AbstractEsAggWrapper<Field, AggWrapper, QueryWrapper, SearchBuilder, AggBuilder>,
        QueryBuilder,
        SearchBuilder,
        AggBuilder>
        implements IEsQueryWrapper<Field, QueryWrapper, AggWrapper, QueryBuilder, SearchBuilder, AggBuilder>, IESearchWrapper<Field, QueryWrapper, SearchBuilder> {

    /**
     * 当前类 this
     */
    protected final QueryWrapper child = (QueryWrapper) this;
    /**
     * 是否校验val的值。true时，val不能为空
     * 关联的EsQueryWrapper全局公用
     */
    protected final AtomicBoolean check;
    /**
     * 当前关系
     */
    protected final Bool bool;
    /**
     * 分页以及排序等条件拼接器
     */
    protected final SearchWrapper searchWrapper;
    /**
     * searchBuilder
     */
    protected final SearchBuilder searchBuilder;
    /**
     * 关系条件map
     */
    protected final Map<BoolEnum, List<QueryBuilder>> queryMap;

    public AbstractEsQueryWrapper(AtomicBoolean check) {
        this.bool = new Bool();
        this.check = check;
        this.searchBuilder = createSearchBuilder();
        this.searchWrapper = createSearchWrapper(null);
        this.queryMap = new HashMap<>();
    }

    public AbstractEsQueryWrapper(AbstractEsQueryWrapper<?, ?, ?, ?, QueryBuilder, SearchBuilder, AggBuilder> queryWrapper) {
        this.bool = queryWrapper.bool;
        this.check = queryWrapper.check;
        this.queryMap = queryWrapper.queryMap;
        this.searchWrapper = createSearchWrapper(queryWrapper.searchWrapper);
        this.searchBuilder = queryWrapper.searchBuilder;
    }

    @Override
    public QueryWrapper and() {
        return changeBool(BoolEnum.MUST);
    }

    @Override
    public QueryWrapper or() {
        return changeBool(BoolEnum.SHOULD);
    }

    @Override
    public QueryWrapper not() {
        return changeBool(BoolEnum.MUST_NOT);
    }

    @Override
    public QueryWrapper filter() {
        return changeBool(BoolEnum.FILTER);
    }

    @Override
    public QueryWrapper custom(QueryBuilder builder) {
        return addQuery(builder);
    }

    @Override
    public QueryWrapper bool(IEsQueryWrapper<?, ?, ?, QueryBuilder, ?, ?> wrapper) {
        return addQuery(wrapper.queryBuilder());
    }

    @Override
    public QueryWrapper bool(Consumer<QueryWrapper> consumer) {
        QueryWrapper wrapper = createWrapper();
        wrapper.and();
        consumer.accept(wrapper);
        return addQuery(wrapper.queryBuilder());
    }

    /**
     * 转换关系
     *
     * @param boolEnum 关系枚举
     * @return this
     */
    private QueryWrapper changeBool(BoolEnum boolEnum) {
        bool.setBoolEnum(boolEnum);
        return child;
    }

    /**
     * 开启空值过滤
     * 子查询调用这个方法会影响外部check
     *
     * @return this
     */
    public QueryWrapper check() {
        check.set(true);
        return child;
    }

    /**
     * 关闭空值过滤
     * 子查询调用这个方法会影响外部check
     *
     * @return this
     */
    public QueryWrapper noCheck() {
        check.set(false);
        return child;
    }

    @SafeVarargs
    @Override
    public final QueryWrapper multiMatch(String val, Field... fields) {
        return IEsQueryWrapper.super.multiMatch(val, fields);
    }

    @SafeVarargs
    @Override
    public final QueryWrapper multiMatch(String val, Float boost, Field... fields) {
        return IEsQueryWrapper.super.multiMatch(val, boost, fields);
    }

    @SafeVarargs
    @Override
    public final QueryWrapper multiMatch(String val, OperatorEnum operator, Field... fields) {
        return IEsQueryWrapper.super.multiMatch(val, operator, fields);
    }

    @SafeVarargs
    @Override
    public final QueryWrapper multiMatch(String val, Float boost, OperatorEnum operator, Field... fields) {
        return IEsQueryWrapper.super.multiMatch(val, boost, operator, fields);
    }

    @Override
    public QueryWrapper from(Integer from) {
        searchWrapper.from(from);
        return child;
    }

    @Override
    public QueryWrapper size(Integer size) {
        searchWrapper.size(size);
        return child;
    }

    @Override
    public QueryWrapper limit(Integer from, Integer size) {
        searchWrapper.limit(from, size);
        return child;
    }

    @Override
    public QueryWrapper select(Collection<String> fields) {
        searchWrapper.select(fields);
        return child;
    }

    @Override
    public QueryWrapper noSelect(Collection<String> fields) {
        searchWrapper.noSelect(fields);
        return child;
    }

    @Override
    public QueryWrapper orderByAsc(Object missing, Collection<String> fields) {
        searchWrapper.orderByAsc(missing, fields);
        return child;
    }

    @SafeVarargs
    @Override
    public final QueryWrapper select(Field... field) {
        searchWrapper.select(field);
        return child;
    }

    @SafeVarargs
    @Override
    public final QueryWrapper noSelect(Field... field) {
        searchWrapper.noSelect(field);
        return child;
    }

    @SafeVarargs
    @Override
    public final QueryWrapper orderByAsc(Field... field) {
        searchWrapper.orderByAsc(field);
        return child;
    }

    @SafeVarargs
    @Override
    public final QueryWrapper orderByDesc(Field... field) {
        searchWrapper.orderByDesc(field);
        return child;
    }

    @SafeVarargs
    @Override
    public final QueryWrapper orderByAsc(Object missing, Field... field) {
        searchWrapper.orderByAsc(missing, field);
        return child;
    }

    @SafeVarargs
    @Override
    public final QueryWrapper orderByDesc(Object missing, Field... field) {
        searchWrapper.orderByDesc(missing, field);
        return child;
    }

    @SafeVarargs
    @Override
    public final QueryWrapper highlight(Field... field) {
        searchWrapper.highlight(field);
        return child;
    }

    @SafeVarargs
    @Override
    public final QueryWrapper highlight(Integer fragmentSize, Integer numberOfFragments, Field... field) {
        searchWrapper.highlight(fragmentSize, numberOfFragments, field);
        return child;
    }

    @Override
    public QueryWrapper orderByDesc(Object missing, Collection<String> fields) {
        searchWrapper.orderByDesc(missing, fields);
        return child;
    }

    @Override
    public QueryWrapper trackTotalHits(Boolean enabled) {
        searchWrapper.trackTotalHits(enabled);
        return child;
    }

    @Override
    public QueryWrapper trackTotalHits(Integer count) {
        searchWrapper.trackTotalHits(count);
        return child;
    }

    @Override
    public QueryWrapper collapse(Field field) {
        searchWrapper.collapse(field);
        return child;
    }

    @Override
    public QueryWrapper highlight(Integer fragmentSize, Integer numberOfFragments, Collection<String> fields) {
        searchWrapper.highlight(fragmentSize, numberOfFragments, fields);
        return child;
    }

    @Override
    public QueryWrapper highlightPreTags(String... val) {
        searchWrapper.highlightPreTags(val);
        return child;
    }

    @Override
    public QueryWrapper highlightPostTags(String... val) {
        searchWrapper.highlightPostTags(val);
        return child;
    }

    @Override
    public void addSearch(SearchBuilder searchBuilder) {
        searchWrapper.addSearch(searchBuilder);
    }

    @Override
    public QueryWrapper customSearch(Consumer<SearchBuilder> consumer) {
        searchWrapper.customSearch(consumer);
        return child;
    }

    /**
     * 添加条件
     *
     * @param queryBuilder queryBuilder
     * @return this
     */
    protected QueryWrapper addQuery(QueryBuilder queryBuilder) {
        if (Objects.isNull(queryBuilder)) {
            return child;
        }
        List<QueryBuilder> queryList = queryMap.computeIfAbsent(bool.getBoolEnum(), k -> new ArrayList<>());
        queryList.add(queryBuilder);
        return child;
    }

    @Override
    public QueryWrapper agg(Consumer<AggWrapper> wrapper) {
        if (Objects.isNull(wrapper)) {
            return child;
        }
        AggWrapper aggWrapper = createAggWrapper();
        wrapper.accept(aggWrapper);
        aggWrapper.addAgg(searchBuilder);
        return child;
    }

    /**
     * 获取当前SearchBuilder
     *
     * @return SearchBuilder
     */
    public SearchBuilder getSearchBuilder() {
        return searchBuilder;
    }

    /**
     * 创建子类
     *
     * @return this
     */
    protected abstract QueryWrapper createWrapper();

    /**
     * 创建聚合拼接器
     *
     * @return AggWrapper
     */
    protected abstract AggWrapper createAggWrapper();

    /**
     * 创建限制拼接器
     *
     * @param searchWrapper 原拼接器
     * @return SearchWrapper
     */
    protected abstract SearchWrapper createSearchWrapper(AbstractEsSearchWrapper<?, ?, SearchBuilder> searchWrapper);

    /**
     * 创建SearchBuilder
     *
     * @return SearchBuilder
     */
    protected abstract SearchBuilder createSearchBuilder();

}
